function ElementZoomer() {
  var oSelectedElement = null, 
      bZoomed = false,
      aoModifiedElements = [],
      adasModifiedElementStyles = [],
      oPreviouslyFocusedElement = null,
      dasZoomSelectedElementStyles = {
        "z-index":      ["9999",    "!important"],
        "position":     ["fixed",   "!important"],
        "width":        ["100%",    "!important"],
        "height":       ["100%",    "!important"],
        "top":          ["0px",     "!important"],
        "left":         ["0px",     "!important"],
        "border":       ["0px",     "!important"],
        "padding":      ["0px",     "!important"],
        "margin":       ["0px",     "!important"],
        "overflow":     ["hidden",  "!important"],
        "visibility":   ["visible", "!important"],
      },
      dasZoomOtherElementStyles = {
        "z-index":      ["0",       "!important"],
        "overflow":     ["hidden",  "!important"],
        "visibility":   ["hidden",  "!important"],
      },
      oPageActionIcon = new ElementZoomerPageActionIcon(this);

  function exchangeStyle(oElement, dasNewStyle) {
    dasOldStyle = {};
    for (var sPropertyName in dasNewStyle) {
      var sNewPropertyValue = dasNewStyle[sPropertyName][0],
          sNewPropertyPriority = dasNewStyle[sPropertyName][1],
          sOldPropertyValue = oElement.style.getPropertyValue(sPropertyName),
          sOldPropertyPriority = oElement.style.getPropertyPriority(sPropertyName);
      dasOldStyle[sPropertyName] = [sOldPropertyValue, sOldPropertyPriority];
      oElement.style.setProperty(sPropertyName, sNewPropertyValue, sNewPropertyPriority);
    }
    return dasOldStyle;
  }
  
  this.selectElement = function(oElement) {
    if (oSelectedElement == oElement) return false;
    oSelectedElement = oElement;
    oPageActionIcon.update();
    return true;
  }
  
  this.deselectElement = function () {
    if (oSelectedElement == null) return false;
    oSelectedElement = null;
    oPageActionIcon.update();
    return true;
  }
  
  this.isZoomed = function () {
    return bZoomed;
  }
  
  this.isElementZoomable = function (oElement) {
    if (oElement == null) return false;
    return oElement.tagName.toLowerCase() in {"canvas": 1, "embed":1, "img":1, "object":1, "video":1};
  }
  this.canZoom = function() {
    return this.isElementZoomable(oSelectedElement);
  }
  
  this.zoomElement = function (oElement) {
    if (!isZoomableElement(oElement)) return false;
    if (bZoomed) restoreZoomedElement();
    oSelectedElement = oElement;
    this.zoom();
    return true;
  }
  
  this.zoom = function () {
    if (bZoomed) return false;
    // Set the styles of all elements to zoom one element and hide the rest.
    // Save all the original styles so they can be restored.
    aoModifiedElements = document.getElementsByTagName("*");
    adasModifiedElementStyles = [];
    for (var i = 0; i < aoModifiedElements.length; i++) {
      var dasNewStyle = aoModifiedElements[i] == oSelectedElement ? dasZoomSelectedElementStyles : dasZoomOtherElementStyles;
      adasModifiedElementStyles[i] = exchangeStyle(aoModifiedElements[i], dasNewStyle);
    }
    if (oSelectedElement != document.activeElement) {
      oPreviouslyFocusedElement = document.activeElement;
      oSelectedElement.focus();
    }
    bZoomed = true;
    oPageActionIcon.update();
    return true;
  }

  this.unZoom = function () {
    if (!bZoomed) return false;
    for (var i = 0; i < aoModifiedElements.length; i++) {
      exchangeStyle(aoModifiedElements[i], adasModifiedElementStyles[i]);
    }
    aoModifiedElements = [];
    adasModifiedElementStyles = [];
    if (oPreviouslyFocusedElement != document.activeElement) {
      oPreviouslyFocusedElement.focus();
    }
    oPreviouslyFocusedElement = null;
    bZoomed = false;
    oPageActionIcon.update();
    return true;
  }
  
  this.changeZoom = function () {
    return bZoomed ? this.unZoom() : this.zoom();
    if (isZoomed()) {
      restoreZoomedElement();
      if (fSuccessCallback) fSuccessCallback();
    } else if (isZoomableElement(oSelectedElement)) {
      zoomElement(oSelectedElement);
      if (fSuccessCallback) fSuccessCallback();
    } else {
      debugLog("Zoom change requested, but nothing to zoom or restore.");
      if (fFailureCallback) fFailureCallback("Zoom change requested, but nothing to zoom or restore.");
    }
    aoModifiedElements = [];
    adasModifiedElementStyles = [];
  }
}
